﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.IO;

namespace DeepTime.BootStrapper.Utilities
{
    public class ImageUploader : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Implementation

        /// <summary>
        /// Property Changed Event
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Runs when the event is Fired
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnPropertyChanged(object sender, string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        #region UploadImageCompleted Event Members & Methods

        /// <summary>
        /// Occurs when items have finish loading
        /// </summary>
        public event EventHandler<UploadImageCompletedEventArgs> UploadImageCompleted;

        /// <summary>
        /// Runs before the event is fired
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnUploadImageCompletedCompleted(object sender, UploadImageCompletedEventArgs e)
        {
            if (UploadImageCompleted != null)
            {
                UploadImageCompleted(sender, e);
            }
        }

        /// <summary>
        /// Clears the event handlers
        /// </summary>
        public void CleaUploadImageCompletedEventHandlers()
        {
            if (UploadImageCompleted != null)
            {
                foreach (EventHandler<UploadImageCompletedEventArgs> eventDelegate in UploadImageCompleted.GetInvocationList())
                {
                    UploadImageCompleted -= eventDelegate;
                }
            }
        }

        #endregion UploadImageCompleted Event Members & Methods

        #region Class Members

        private bool _IsUploading;
        private double _Progress;
        private UserControl _ThreadContext = new UserControl();

        #endregion Class Members

        #region Constructor

        public ImageUploader()
        {
            _IsUploading = false;
            _Progress = 0;
        }

        #endregion Constructor

        #region Properties

        /// <summary>
        /// Indicates if an upload is in progress
        /// </summary>
        public bool IsUploading
        {
            get
            {
                return _IsUploading;
            }
            private set
            {
                _IsUploading = value;
                OnPropertyChanged(this, "IsUploading");
            }
        }

        /// <summary>
        /// Represents the progress of the current upload
        /// </summary>
        public double Progress
        {
            get
            {
                return _Progress;
            }
            private set
            {
                _Progress = value;
                OnPropertyChanged(this, "Progress");
            }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Uploads an image
        /// </summary>
        /// <param name="file">Reference to file to upload.</param>
        public void Upload(FileInfo file, string destinationUrl)
        {
            FileStream fileStream = file.OpenRead();

            //
            // Set state
            //
            IsUploading = true;
            Progress = 0;

            //
            // Create Webrequest
            //
            HttpWebRequest wr = WebRequest.CreateHttp(destinationUrl);
            wr.AllowWriteStreamBuffering = false;
            wr.Method = "POST";
            wr.ContentLength = fileStream.Length;

            wr.BeginGetRequestStream(new AsyncCallback(result =>
            {
                Stream s = wr.EndGetRequestStream(result);

                //
                // Write Data to stream
                //
                int bufferSize = 32768;
                long bytesWritten = 0;

                while (bytesWritten < fileStream.Length)
                {
                    //
                    // Read chunk from file
                    //
                    int bytesToRead = Math.Min(bufferSize, (int)(fileStream.Length - bytesWritten));
                    byte[] ChunkData = new byte[bytesToRead];
                    fileStream.Read(ChunkData, 0, bytesToRead);

                    //
                    // Write chunk to http stream
                    //
                    s.Write(ChunkData, 0, ChunkData.Length);
                    s.Flush();
                    bytesWritten += ChunkData.Length;

                    _ThreadContext.Dispatcher.BeginInvoke(() =>
                    {
                        Progress = Math.Round(Math.Max((double)bytesWritten, 1) / Math.Max((double)fileStream.Length, 1) * 100, 2);
                    });
                }

                s.Close();

                wr.BeginGetResponse(ReadHttpResponseCallback, wr);
            }), wr);
        }

        /// <summary>
        /// Handles the response callback
        /// </summary>
        /// <param name="asynchronousResult"></param>
        private void ReadHttpResponseCallback(IAsyncResult asynchronousResult)
        {
            UploadImageCompletedEventArgs args;

            try
            {
                //
                // Get http response
                //
                HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
                HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);

                //
                // Read response
                //
                StreamReader reader = new StreamReader(webResponse.GetResponseStream());
                string response = reader.ReadToEnd();
                reader.Close();

                //
                // Process response
                //
                string[] responseData = response.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                if (responseData.Length == 0)
                {
                    args = new UploadImageCompletedEventArgs()
                    {
                        Error = "Unexpected response.",
                        //FileName = "",
                        Successful = false
                    };
                }
                else if (responseData[0] == "success" && responseData.Length == 2)
                {
                    args = new UploadImageCompletedEventArgs()
                    {
                        Error = "",
                        //FileName = responseData[1],
                        Successful = true
                    };
                }
                else if (responseData[0] == "error")
                {
                    args = new UploadImageCompletedEventArgs()
                    {
                        Error = (responseData.Length == 2) ? responseData[1] : "Unknown error.",
                        //FileName = "",
                        Successful = false
                    };
                }
                else
                {
                    args = new UploadImageCompletedEventArgs()
                    {
                        Error = "Unexpected response.",
                        //FileName = "",
                        Successful = false
                    };
                }

            }
            catch
            {
                args = new UploadImageCompletedEventArgs()
                {
                    Error = "Error in reading response.",
                    //FileName = "",
                    Successful = false
                };
            }

            _ThreadContext.Dispatcher.BeginInvoke(() =>
            {
                //
                // Set state
                //
                IsUploading = false;
                Progress = 100;

                //
                // Raise completed event
                //
                OnUploadImageCompletedCompleted(this, args);
            });
        }

        #endregion Methods
    }
}
